import { isNative,isIE } from "./env"

export let isUsingMicroTask = false

const callbacks = []
let pending = false

function flushCallbacks() {
  pending = false
  const copies = callbacks.slice(0)
  callbacks.length = 0
  for (let i = 0; i < copies.length; i++) {
    copies[i]()
  }
}
  // 这里我们有使用微任务的异步延迟包装器。
  // 在2.5中，我们使用了（宏）任务（与微任务相结合）。
  // 然而，当状态在重新绘制之前发生更改时，它会出现一些微妙的问题
  // （例如#6813，在转换中输出）。
  // 此外，在事件处理程序中使用（宏）任务会导致一些奇怪的行为
  // 无法绕过的（例如#7109、#7153、#7546、#7834、#8109）。
  // 所以我们现在再次在任何地方使用微任务。
  // 这种权衡的一个主要缺点是存在一些场景
  // 其中微任务的优先级太高，并在两者之间开火
  // 顺序事件（例如#4521、#6690，它们有变通方法）
  // 或者甚至在同一事件的冒泡之间（#6566）。
let timerFunc
  //nextTick行为利用了可以访问的微任务队列
  //通过本地Promise.then或MutationObserver。
  //MutationObserver得到了更广泛的支持，但它在
  //在触摸事件处理程序中触发时，iOS中的UIWebView>=9.3.3。它
  //触发几次后完全停止工作。。。所以，如果是本地的
  //承诺可用，我们将使用它：
  // 处理兼容问题
if (typeof Promise !== 'undefined' && isNative(Promise)) {
  // 如果有Promise
  const p = Promise.resolve()
  timerFunc = () => {
    p.then(flushCallbacks)
    // 在有问题的UIWebViews中，Promise.then并没有完全崩溃，但是
    // 它可能会陷入一种奇怪的状态，回调被推入
    // 微任务队列，但队列没有被刷新，直到浏览器
    // 需要做一些其他工作，例如处理计时器。因此，我们可以
    // 通过添加一个空计时器来“强制”刷新微任务队列。
    // if (isIOS) setTimeout(noop) 
  }
}else if (
  !isIE &&
  typeof MutationObserver !== 'undefined' &&
  (isNative(MutationObserver) ||
    // PhantomJS and iOS 7.x
    MutationObserver.toString() === '[object MutationObserverConstructor]')
){
  // MutationObserver H5的异步方法 他可以监听DOM变化 监控完毕之后再来异步更新
  //  在本地Promise不可用的情况下使用MutationObserver，
  //  例如PhantomJS、iOS7、Android 4.4
  // （#6466 MutationObserver在IE11中不可靠）
  let counter = 1
  const observer = new MutationObserver(flushCallbacks)
  // 创建文本
  const textNode = document.createTextNode(String(counter))
  // 通过observer.observe监听 characterData为true 观察文本的内容
  observer.observe(textNode, {
    characterData: true
  })
  timerFunc = () => {
    counter = (counter + 1) % 2
    textNode.data = String(counter)
  }
  // isUsingMicroTask = true
}else if (typeof setImmediate !== 'undefined' && isNative(setImmediate)) {
  // 回退到setImmediate。
  // 从技术上讲，它利用（宏）任务队列，
  // 但它仍然是比setTimeout更好的选择。
  timerFunc = () => {
    setImmediate(flushCallbacks)
  }
}
/**
 * 
 * @param {function} cb
 * 面试题  nextTick的实现原理
 */
export function nextTick(cb) {
  // 1.vue自己的
  // 2.用户自己的 共2个列队
  // console.log('[ cb ] >', cb)
  let _resolve
  callbacks.push(cb)
  // callbacks.push(() => {
  //   if (cb) {
  //     try {
  //       cb.call(ctx)
  //     } catch (e) {
  //       handleError(e, ctx, 'nextTick')
  //     }
  //   } else if (_resolve) {
  //     _resolve(ctx)
  //   }
  // })
  if (!pending) {
    pending = true
    timerFunc() // 异步方法 但是要处理兼容
  }
  // // $flow-disable-line
  // if (!cb && typeof Promise !== 'undefined') {
  //   return new Promise(resolve => {
  //     _resolve = resolve
  //   })
  // }
}
